using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using OhioState.AssetManaged;
using OhioState.Graphics;
using OhioState.Graphics.OpenGL;
using OhioState.Graphics.Proxy;

namespace OhioState.Controls
{
    public partial class TwoDTransferFunctionCtrl : UserControl
    {
        private DrawingPanel mainPanel;
        private Bitmap histogramBitmap;
        private IAssetManagedHandler histogramHandler;
        private IAssetManagedHandler histogramGradientHandler;
        
        private ArrayList computedHistogramsList;
        private ArrayList computedHistogramsBitmaps;
        
        private ArrayList computedHistogramsListGrad;
        private ArrayList computedHistogramsBitmapsGrad;
        
        public TwoDTransferFunctionCtrl()
        {
            InitializeComponent();
            CreateMainPanel();
            colorChooserCtrl1.AssetManagerCtrl.Init(typeof(Volume));
            histogramHandler = new IAssetManagedHandler(SelectedEventHandler);
            histogramGradientHandler = new IAssetManagedHandler(SelectedEventGradientEqualization);
            colorChooserCtrl1.AssetManagerCtrl.SelectedEvent += histogramHandler;
            //colorChooserCtrl1.AssetManagerCtrl.SelectedEvent += new IAssetManagedHandler(SelectedEventGradientEqualization);
            //colorChooserCtrl1.AssetManagerCtrl.SelectedEvent += new IAssetManagedHandler(SelectedEventHandler);
            colorChooserCtrl1.EqualizeGradientsHistogramCheckBox.CheckedChanged += new EventHandler(EqualizeGradientsHistogramCheckBox_CheckedChanged);
            colorChooserCtrl1.TransferFunction = this;
            computedHistogramsList = new ArrayList();
            computedHistogramsBitmaps = new ArrayList();
            computedHistogramsListGrad = new ArrayList();
            computedHistogramsBitmapsGrad = new ArrayList();
        }

        private void EqualizeGradientsHistogramCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if ((sender as CheckBox).Checked)
            {
                colorChooserCtrl1.AssetManagerCtrl.SelectedEvent -= histogramHandler;
                colorChooserCtrl1.AssetManagerCtrl.SelectedEvent += histogramGradientHandler;
            }
            else
            {
                colorChooserCtrl1.AssetManagerCtrl.SelectedEvent += histogramHandler;
                colorChooserCtrl1.AssetManagerCtrl.SelectedEvent -= histogramGradientHandler;
            }
            //update the histogram
            colorChooserCtrl1.AssetManagerCtrl.SelectItem(colorChooserCtrl1.AssetManagerCtrl.GetSelected());
        }
   
        private void CreateMainPanel()
        {
            this.SuspendLayout();
            this.mainPanel = new DrawingPanel(this);
            mainPanel.Size = new Size(200, 200);
            mainPanel.Location = new Point(5, 5);
            this.Controls.Add(mainPanel);
            this.ResumeLayout(false);
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            if (mainPanel != null)
            {
                int min = System.Math.Min(this.Width, this.Height);
                mainPanel.Size = new Size(min - 10, min - 10);
            }
        }

        public void SetHistogramBitmap(Bitmap histogramBitmap)
        {
            this.histogramBitmap = histogramBitmap;
        }

        public void MarkerSelectionChanged(int markerIndex, Color markerColor)
        {
            colorChooserCtrl1.SetColor(markerColor.R, markerColor.G, markerColor.B, markerColor.A);
        }

        public void MarkerColorChanged(Color newColor)
        {
            mainPanel.SetSelectedMarkerColor(newColor);
        }


        //TODO: move this code somewhere - it should not be in the event handler, I suppose.

        /// <summary>
        /// Create the two-dimensional histogram - process volume dataset - calculate gradients, etc.
        /// </summary>
        private void SelectedEventHandler(IAssetManaged obj)
        {
            //try
            {
                if (obj.GetType().Equals(typeof(Volume)))
                {
                    if (computedHistogramsList.Contains(obj.Name))
                    {
                        int index = computedHistogramsList.IndexOf(obj.Name);
                        mainPanel.SetHistogram((Bitmap)computedHistogramsBitmaps[index]);
                        mainPanel.Invalidate();
                        return;
                    }
                    computedHistogramsList.Add(obj.Name);
                    
                    Volume volume = (Volume)obj;
                    int format = TextureManager.PixelFormat[volume.TexelType.Format];
                    int type = TextureManager.PixelBuffer[volume.TexelType.Type];
                    byte[] byte_buffer = null;
                    uint[] uint_buffer = null;
                    float[] float_buffer = null;
                    int xDim = 0, yDim = 0, zDim = 0;
                    switch (volume.TexelType.Type)
                    {
                        case ByteFormat.BYTE:
                            byte_buffer = volume.GetTextureByte(out xDim, out yDim, out zDim);
                            break;
                        case ByteFormat.INT:
                            uint_buffer = volume.GetTextureInt(out xDim, out yDim, out zDim);
                            break;
                        case ByteFormat.SHORT:
                            byte_buffer = volume.GetTextureShort(out xDim, out yDim, out zDim);
                            break;
                        case ByteFormat.FLOAT:
                            float_buffer = volume.GetTextureFloat(out xDim, out yDim, out zDim);
                            break;
                    }

                    //compute histogram here
                    int[,] result = new int[256, 256];
                    if (byte_buffer != null || byte_buffer.Length > 0)
                    {
                        //int[] gradient_percentiles = new int[256];
                        byte v000 = 0, v100 = 0, v010 = 0, v001 = 0;
                        for (int k = 0; k < zDim; k++)
                        {
                            for (int j = 0; j < yDim; j++)
                            {
                                for (int i = 0; i < xDim; i++)
                                {
                                    if ((k + 1) * xDim * yDim + (j + 1) * xDim + i + 1 < xDim * yDim * zDim)
                                    {
                                        v000 = byte_buffer[k * xDim * yDim + j * xDim + i];
                                        v100 = byte_buffer[k * xDim * yDim + j * xDim + i + 1];
                                        v010 = byte_buffer[k * xDim * yDim + (j + 1) * xDim + i];
                                        //v110 = byte_buffer[k * xDim * yDim + (j + 1) * xDim + i + 1];
                                        v001 = byte_buffer[(k + 1) * xDim * yDim + j * xDim + i];
                                        //v101 = byte_buffer[(k + 1) * xDim * yDim + j * xDim + i + 1];
                                        //v011 = byte_buffer[(k + 1) * xDim * yDim + (j + 1) * xDim + i];
                                        //v111 = byte_buffer[(k + 1) * xDim * yDim + (j + 1) * xDim + i + 1];
                                        int gradient_x = v100 - v000;
                                        int gradient_y = v010 - v000;
                                        int gradient_z = v001 - v000;
                                        int gradient_value = (int)System.Math.Sqrt(
                                            gradient_x * gradient_x + gradient_y * gradient_y + gradient_z * gradient_z);
                                        if (gradient_value > 255) gradient_value = 255;//hack! should normalize instead
                                        result[v000, gradient_value]++;
                                    }
                                }
                            }
                        }
                    }
                    //TODO: add other data types (uint, etc.)

                    histogramBitmap = new Bitmap(256, 256);
                    for (int i = 0; i < 256; i++)
                    {
                        for (int j = 0; j < 256; j++)
                        {
                            if (result[i, j] > 255) result[i, j] = 255;
                            histogramBitmap.SetPixel(i, 255 - j, Color.FromArgb(result[i, j], result[i, j], result[i, j]));
                            //debug!
                            //histogramBitmap.SetPixel(i, j, Color.FromArgb(result[i, j], result[i, j], result[i, j]));
                        }
                    }
                    computedHistogramsBitmaps.Add(histogramBitmap);
                    mainPanel.SetHistogram(histogramBitmap);
                    mainPanel.Invalidate();//force redraw
                }
            }
            //catch(Exception e)
            {
            //    MessageBox.Show("Exception occured in 2D Transfer Function class!");
            }
        }

        private void SelectedEventGradientEqualization(IAssetManaged obj)
        {
            if (obj == null) return;
            if (!obj.GetType().Equals(typeof(Volume)))
                return;

            if (computedHistogramsListGrad.Contains(obj.Name))
            {
                int index = computedHistogramsListGrad.IndexOf(obj.Name);
                mainPanel.SetHistogram((Bitmap)computedHistogramsBitmapsGrad[index]);
                mainPanel.Invalidate();
                return;
            }
            computedHistogramsListGrad.Add(obj.Name);

            Volume volume = (Volume)obj;
            int format = TextureManager.PixelFormat[volume.TexelType.Format];
            int type = TextureManager.PixelBuffer[volume.TexelType.Type];
            byte[] byte_buffer = null;
            uint[] uint_buffer = null;
            float[] float_buffer = null;
            int xDim = 0, yDim = 0, zDim = 0;
            switch (volume.TexelType.Type)
            {
                case ByteFormat.BYTE:
                    byte_buffer = volume.GetTextureByte(out xDim, out yDim, out zDim);
                    break;
                case ByteFormat.INT:
                    uint_buffer = volume.GetTextureInt(out xDim, out yDim, out zDim);
                    break;
                case ByteFormat.SHORT:
                    byte_buffer = volume.GetTextureShort(out xDim, out yDim, out zDim);
                    break;
                case ByteFormat.FLOAT:
                    float_buffer = volume.GetTextureFloat(out xDim, out yDim, out zDim);
                    break;
            }

            //calculate gradient histogram
            double[] bins = new double[256];

            int total_num_values = 0;
            if (byte_buffer != null || byte_buffer.Length > 0)
            {
                byte v000 = 0, v100 = 0, v010 = 0, v001 = 0;
                for (int k = 0; k < zDim; k++)
                {
                    for (int j = 0; j < yDim; j++)
                    {
                        for (int i = 0; i < xDim; i++)
                        {
                            if ((k + 1) * xDim * yDim + (j + 1) * xDim + i + 1 < xDim * yDim * zDim)
                            {
                                v000 = byte_buffer[k * xDim * yDim + j * xDim + i];
                                v100 = byte_buffer[k * xDim * yDim + j * xDim + i + 1];
                                v010 = byte_buffer[k * xDim * yDim + (j + 1) * xDim + i];
                                //v110 = byte_buffer[k * xDim * yDim + (j + 1) * xDim + i + 1];
                                v001 = byte_buffer[(k + 1) * xDim * yDim + j * xDim + i];
                                //v101 = byte_buffer[(k + 1) * xDim * yDim + j * xDim + i + 1];
                                //v011 = byte_buffer[(k + 1) * xDim * yDim + (j + 1) * xDim + i];
                                //v111 = byte_buffer[(k + 1) * xDim * yDim + (j + 1) * xDim + i + 1];
                                int gradient_x = v100 - v000;
                                int gradient_y = v010 - v000;
                                int gradient_z = v001 - v000;
                                int gradient_value = (int)System.Math.Sqrt(
                                    gradient_x * gradient_x + gradient_y * gradient_y + gradient_z * gradient_z);
                                if (gradient_value > 255) gradient_value = 255;//hack! should normalize instead
                                //result[v000, gradient_value]++;

                                bins[gradient_value]++;

                                total_num_values++;
                            }
                        }
                    }
                }

            }

            double[] cdf = new double[256];
            for (int i = 0; i < 256; i++)
            {
                for (int j = 0; j <= i; j++)//should it be less than or just less?
                {
                    cdf[i] += bins[j];
                }
            }
            double cdf_min = cdf[0];//255;
            double cdf_max = cdf[0];//0;
            for (int i = 0; i < cdf.Length; i++)
            {
                if (cdf_min > cdf[i]) cdf_min = cdf[i];
                if (cdf_max < cdf[i]) cdf_max = cdf[i];
            }

            int num_values = total_num_values;//volume.Width * volume.Height * volume.Depth;//number of voxels in a volume
            for (int i = 0; i < 256; i++)
            {
                cdf[i] = 255 * (cdf[i] - cdf_min) / (num_values - cdf_min);
            }
            //end calculating gradient histogram

            int[,] result = new int[256, 256];
            if (byte_buffer != null || byte_buffer.Length > 0)
            {
                byte v000 = 0, v100 = 0, v010 = 0, v001 = 0;
                for (int k = 0; k < zDim; k++)
                {
                    for (int j = 0; j < yDim; j++)
                    {
                        for (int i = 0; i < xDim; i++)
                        {
                            if ((k + 1) * xDim * yDim + (j + 1) * xDim + i + 1 < xDim * yDim * zDim)
                            {
                                v000 = byte_buffer[k * xDim * yDim + j * xDim + i];
                                v100 = byte_buffer[k * xDim * yDim + j * xDim + i + 1];
                                v010 = byte_buffer[k * xDim * yDim + (j + 1) * xDim + i];
                                //v110 = byte_buffer[k * xDim * yDim + (j + 1) * xDim + i + 1];
                                v001 = byte_buffer[(k + 1) * xDim * yDim + j * xDim + i];
                                //v101 = byte_buffer[(k + 1) * xDim * yDim + j * xDim + i + 1];
                                //v011 = byte_buffer[(k + 1) * xDim * yDim + (j + 1) * xDim + i];
                                //v111 = byte_buffer[(k + 1) * xDim * yDim + (j + 1) * xDim + i + 1];
                                int gradient_x = v100 - v000;
                                int gradient_y = v010 - v000;
                                int gradient_z = v001 - v000;
                                int gradient_value = (int)System.Math.Sqrt(
                                    gradient_x * gradient_x + gradient_y * gradient_y + gradient_z * gradient_z);
                                if (gradient_value > 255) gradient_value = 255;

                                //instead of using calculated value, use normalized one
                                //result[v000, gradient_value]++;
                                int newValue = (int)(cdf[gradient_value]); // * 255); //WHY MULTIPLY by 255? the values are already in range 0-255!
                                if (newValue > 255) newValue = 255; //should not happen
                                result[v000, newValue]++;
                            }
                        }
                    }
                }
            }

            //debug - try to stretch the bitmap
            //Bitmap stretchedBitmap = new Bitmap(256, 256);
            //for (int i = 0; i < 256; i++)
            //{
            //    for (int j = 0; j < 256; j++)
            //    {
            //        stretchedBitmap.SetPixel(i, j, Color.Black);
            //    }
            //}
            //try
            //{
            //    int cutoff = 123;//155
            //    for (int i = 0; i < 256; i++)
            //    {
            //        for (int j = cutoff; j < 256; j++)//256; j++)
            //        {
            //            int k = (int)((float)j - cutoff) * 255 / (255 - cutoff);
            //            int v = result[i, j];
            //            if (v > 255) v = 255;
            //            stretchedBitmap.SetPixel(i, 255 - k, Color.FromArgb(v, v, v));
            //        }
            //    }
            //}
            //catch (Exception e)
            //{
            //    System.Console.WriteLine(e.StackTrace);
            //}
            
            histogramBitmap = new Bitmap(256, 256);
            for (int i = 0; i < 256; i++)
            {
                for (int j = 0; j < 256; j++)
                {
                    if (result[i, j] > 255) result[i, j] = 255;
                    histogramBitmap.SetPixel(i, 255 - j, Color.FromArgb(result[i, j], result[i, j], result[i, j]));
                    //debug!
                    //histogramBitmap.SetPixel(i, j, Color.FromArgb(result[i, j], result[i, j], result[i, j]));
                }
            }
            computedHistogramsBitmapsGrad.Add(histogramBitmap);
            mainPanel.SetHistogram(histogramBitmap);
            //computedHistogramsBitmapsGrad.Add(stretchedBitmap);
            //mainPanel.SetHistogram(stretchedBitmap);
            mainPanel.Invalidate();
        }

        public void CreateTransferFunction()
        {
            mainPanel.CreateTransferFunction();
        }
    }
}